﻿#region File Header
//-----------------------------------------------------------------------------
// SibLib
//
// Copyright (C) 2010 Julien Villers
// This program is distributed under the terms of the 
// GNU Lesser General Public License (LGPL).
// See Docs/lgpl.txt and Docs/gpl.txt
//-----------------------------------------------------------------------------
#endregion

using System.Collections.Generic;

namespace SibLib.Containers
{
    /// <summary>
    /// This kind of pool is enumerable, and meant to be safely updated during enumeration.
    /// </summary>
    /// <typeparam name="T">A class type, with a default constructor required to populate the pool</typeparam>
    public class AutoPool<T> where T: class, new()
    {
        #region Members
        private List<T> m_ListToAdd;
        private List<T> m_ListActive;
        private List<T> m_ListToRemove;
        private List<T> m_ListAvailable;
        #endregion

        #region Properties
        public T this[int index]
        {
            get { return m_ListActive[index]; }
            set { m_ListActive[index] = value; }
        }

        public int Count
        {
            get { return m_ListActive.Count; }
        }

        public int Available
        {
            get { return m_ListAvailable.Count; }
        }

        public int Capacity
        {
            get { return m_ListActive.Capacity; }
        }
        #endregion

        #region Constructor
        public AutoPool(int capacity)
        {
            m_ListActive = new List<T>(capacity);
            m_ListAvailable = new List<T>(capacity);
            m_ListToAdd = new List<T>(capacity / 2);
            m_ListToRemove = new List<T>(capacity / 2);

            FillPool();
        }
        #endregion

        private void FillPool()
        {
            for (int i = 0; i != m_ListAvailable.Capacity; i++)
            {
                m_ListAvailable.Add(new T());
            }
        }

        #region Content API

        public T AddNewItem()
        {
            if (m_ListAvailable.Count == 0)
                return null;

            T item = m_ListAvailable[0];
            m_ListAvailable.RemoveAt(0);
            m_ListToAdd.Add(item);
            return item;
        }

        public void Remove(T item)
        {
            m_ListToRemove.Add(item);
        }

        public void RemoveAt(int index)
        {
            m_ListToRemove.Add(m_ListActive[index]);
        }

        public void Clear()
        {
            m_ListToAdd.Clear();
            m_ListToRemove.AddRange(m_ListActive);
        }
        #endregion

        #region Public API - Update
        public void ProcessAdds()
        {
            foreach (T item in m_ListToAdd)
            {
                m_ListActive.Add(item);
            }
            m_ListToAdd.Clear();
        }

        public void ProcessRemoves()
        {
            foreach (T item in m_ListToRemove)
            {
                // If item is successfully removed (ie it must exist), it is put back into pool.
                if (m_ListActive.Remove(item))
                    m_ListAvailable.Add(item);
            }
            m_ListToRemove.Clear();
        }
        #endregion

        #region Enumeration
        /// <summary>
        /// Enumerator.
        /// </summary>
        /// <returns></returns>
        public List<T>.Enumerator GetEnumerator()
        {
            return m_ListActive.GetEnumerator();
        }

        #endregion
    }
}
